/**
 * (C) Andrew Page 
 * \file ObjectSAXParser.cc 
 * Implementation file for core SAX parser functionality 
 */
#include <stdlib.h>
#include <string.h>
#include "ObjectSAXParser.hh"

namespace objectsaxparser {

ObjectSAXParser::parserStackEntry::~parserStackEntry()
{
  delete [] data ;
}

ObjectSAXParser::ObjectSAXParser()
{
    
}

ObjectSAXParser::~ObjectSAXParser()
{
}

void ObjectSAXParser::_startDocument(void *ctx)
{
  ObjectSAXParser *self = static_cast<ObjectSAXParser *>(ctx) ;
  self->startDocument() ;
}

void ObjectSAXParser::startDocument()
{
}

void ObjectSAXParser::_endDocument(void *ctx)
{
  ObjectSAXParser *self = static_cast<ObjectSAXParser *>(ctx) ;
  self->endDocument() ;
}

void ObjectSAXParser::endDocument()
{
}

void ObjectSAXParser::warning(void *ctx, const char *msg, ...)
{
    va_list args ;

    va_start(args, msg) ;
    ObjectSAXParser *self = static_cast<ObjectSAXParser *>(ctx) ;

    self->vwarning(msg, args) ;

    va_end(args) ;
}

void ObjectSAXParser::vwarning(const char *msg, va_list args)
{
  vfprintf(stderr, msg, args) ;
}

void ObjectSAXParser::error(void *ctx, const char *msg, ...)
{
    va_list args ;

    va_start(args, msg) ;
    ObjectSAXParser *self = static_cast<ObjectSAXParser *>(ctx) ;

    self->verror(msg, args) ;

    va_end(args) ;
}

void ObjectSAXParser::verror(const char *msg, va_list args)
{
    vfprintf(stderr, msg, args) ;
}


void ObjectSAXParser::_characters(void *cxt, 
				 const xmlChar *chars, 
				 int len) 
{
  ObjectSAXParser *self = static_cast<ObjectSAXParser *>(cxt) ;

  self->characters(chars, len) ;
}

void ObjectSAXParser::characters(const xmlChar *chars, 
				 int len) 
{
  parserStackEntry& stackEntry = parserStack.back() ; // current entry on the stack
    
  xmlChar *oldBuffer = stackEntry.data ; // save for deletin'
  stackEntry.data = new xmlChar[stackEntry.data_length + len] ;
    
  //!< \todo move to a more efficient scheme
  memcpy(stackEntry.data, oldBuffer, sizeof(xmlChar) * stackEntry.data_length) ;
  memcpy(stackEntry.data+stackEntry.data_length, chars, sizeof(xmlChar) * len) ;
  stackEntry.data_length += len ;

  delete [] oldBuffer ;
}


void ObjectSAXParser::startElement(void *cxt, 
				   const xmlChar *fullName, 
				   const xmlChar **attributes)
{
  ObjectSAXParser *self = static_cast<ObjectSAXParser *>(cxt) ;
  self->parserStack.resize(self->parserStack.size()+1) ;     // push a new entry on the stack(default ctor invoked)
  parserStackEntry& stackEntry = self->parserStack.back() ;  // reference to the dict in the entry
  
    
  attrDict& dict = stackEntry.dict ; // reference to the dict in the entry
  if (attributes != NULL ) {
    // attributes appear as name0,value0,name1,value1...nameN, valueN, NULL 
    int i = 0 ;
    while ( 1 ) {
      if (attributes[i] == NULL ) 
	break ;
      dict[(const char *)attributes[i]] = (const char *)attributes[i+1] ; // add to the dict
      i += 2 ;
            
    } /* while */
  } /* if */

  string name = (const char *)fullName ;
  startHandlerMap_t::iterator it = self->startHandlerMap.find(name) ;
  if( it == self->startHandlerMap.end() ) {
    self->parserStack.push_back(stackEntry) ;    
    return ;
  }

  /*!< \TODO wrap up in a try/catch */
  for( start_handler_list_t::iterator lit = it->second.begin() ; lit != it->second.end() ; lit++) {
    (*lit)->tagStart(name) ;
  }

  self->parserStack.push_back(stackEntry) ;
}

void ObjectSAXParser::elementEnd(void *cxt, const xmlChar *tag)
{
    ObjectSAXParser *self = static_cast<ObjectSAXParser *>(cxt) ;
    parserStackEntry& stackEntry = self->parserStack.back() ;

    string name = (const char *)tag ;
    endHandlerMap_t::iterator it = self->endHandlerMap.find(name) ;

    if( it == self->endHandlerMap.end() ) {
      self->parserStack.pop_back() ;
        return ;
    }

    for( end_handler_list_t::iterator lit = it->second.begin() ; lit != it->second.end() ; lit++ ) {
        (*lit)->tagEnd(name, string((const char *)stackEntry.data, stackEntry.data_length), stackEntry.dict) ;
    }
    self->parserStack.pop_back() ;
}


void ObjectSAXParser::reset()
{
    parserStack.clear() ;
}

int ObjectSAXParser::parse(const string& fname)
{
    xmlSAXHandler handler ;

    /*
    * If someone is reusing this parser after parsing broken documents 
    * this will clear out the stack before performing the next parse 
    * operation.   
    */
    reset() ;

    memset(&handler, 0, sizeof(handler)) ;

    handler.startDocument = ObjectSAXParser::_startDocument ;
    handler.endDocument   = ObjectSAXParser::_endDocument ;
    handler.characters    = ObjectSAXParser::_characters ;
    handler.startElement  = ObjectSAXParser::startElement ;
    handler.endElement    = ObjectSAXParser::elementEnd ;
    handler.error         = ObjectSAXParser::error ;
    handler.warning       = ObjectSAXParser::warning ;
    
    return xmlSAXUserParseFile(&handler, this, fname.c_str()) ;
    
}

void ObjectSAXParser::addStartHandler(const string& tag, ITagStart *handler)
{
    startHandlerMap[tag].push_back(handler) ;
}

void ObjectSAXParser::addEndHandler(const string& tag, ITagEnd *handler)
{
    endHandlerMap[tag]. push_back(handler) ;
}

} // objectsaxparser
